package controllers

import (
	"net/http"
	"time"

	"fmt"
	"strconv"

	"gitee.com/golanglearn/small/app"
	"gitee.com/golanglearn/small/models"
	"gitee.com/golanglearn/small/repositories"
	"gitee.com/golanglearn/small/services"
	"gitee.com/golanglearn/small/util"

	"github.com/gorilla/mux"
)

type ThemeController struct {
	*app.App
	repositories.ThemeRepository
	repositories.TaskRepository
	repositories.SubTaskRepository
}

type ThemePaginator struct {
	Total        int     `json:"total"`
	PerPage      int     `json:"perPage"`
	CurrentPage  int     `json:"currentPage"`
	LastPage     int     `json:"lastPage"`
	From         int     `json:"from"`
	To           int     `json:"to"`
	FirstPageUrl string  `json:"firstPageUrl"`
	LastPageUrl  string  `json:"lastPageUrl"`
	NextPageUrl  *string `json:"nextPageUrl"`
	PrevPageUrl  *string `json:"prevPageUrl"`
}

func NewThemeController(a *app.App, tr repositories.ThemeRepository, tar repositories.TaskRepository, star repositories.SubTaskRepository) *ThemeController {
	return &ThemeController{a, tr, tar, star}
}

func (tc *ThemeController) GetAll(w http.ResponseWriter, r *http.Request) {
	httpScheme := "https://"
	total, _ := tc.ThemeRepository.GetTotalThemeCount()
	page := r.URL.Query().Get("page")
	pageInt, err := strconv.Atoi(page)
	if err != nil {
		pageInt = 1
	}
	perPage := r.URL.Query().Get("perpage")
	perPageInt, err := strconv.Atoi(perPage)
	if err != nil || perPageInt < 1 || perPageInt > 100 {
		perPageInt = 10
	}
	offset := (pageInt - 1) * perPageInt
	to := pageInt * perPageInt
	if to > total {
		to = total
	}

	from := offset + 1
	totalPages := (total-1)/perPageInt + 1
	prevPage := pageInt - 1
	firstPageUrl := fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", 1)
	lastPageString := fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", totalPages)
	var prevPageUrl string
	var nextPageUrl string
	if prevPage > 0 && prevPage < totalPages {
		prevPageUrl = fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", prevPage)
	}

	nextPage := pageInt + 1
	if nextPage <= totalPages {
		nextPageUrl = fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", nextPage)
	}

	themes, err := tc.ThemeRepository.Paginate(perPageInt, offset)
	if err != nil {
		NewAPIError(&APIError{false, "Could not fetch theme", http.StatusBadRequest}, w)
		return
	}

	if len(themes) == 0 {
		NewAPIResponse(&APIResponse{Success: false, Message: "Could not find theme", Data: themes}, w, http.StatusNotFound)
		return
	}

	themePaginator := APIPagination{
		total,
		perPageInt,
		pageInt,
		totalPages,
		from,
		to,
		firstPageUrl,
		lastPageString,
		nextPageUrl,
		prevPageUrl,
	}

	NewAPIResponse(&APIResponse{Success: true, Data: themes, Pagination: &themePaginator}, w, http.StatusOK)
}

func (tc *ThemeController) GetById(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		NewAPIError(&APIError{false, "Invalid request", http.StatusBadRequest}, w)
		return
	}
	theme, err := tc.ThemeRepository.FindById(id)
	if err != nil {
		NewAPIError(&APIError{false, "Could not find theme", http.StatusNotFound}, w)
		return
	}

	NewAPIResponse(&APIResponse{Success: true, Data: theme}, w, http.StatusOK)
}

func (tc *ThemeController) Create(w http.ResponseWriter, r *http.Request) {
	uid, err := services.UserIdFromContext(r.Context())
	if err != nil {
		NewAPIError(&APIError{false, "Something went wrong", http.StatusInternalServerError}, w)
		return
	}

	j, err := GetJSON(r.Body)
	if err != nil {
		NewAPIError(&APIError{false, "Invalid request", http.StatusBadRequest}, w)
		return
	}

	name, err := j.GetString("name")
	if err != nil {
		NewAPIError(&APIError{false, "name is required", http.StatusBadRequest}, w)
		return
	}

	name = util.CleanZalgoText(name)

	if len(name) > 30 {
		NewAPIError(&APIError{false, "Title is too Long", http.StatusBadRequest}, w)
		return
	}

	descinfo, err := j.GetString("descinfo")
	if err != nil {
		descinfo = ""
	}

	theme := &models.Theme{
		Name:      name,
		UID:       uid,
		Descinfo:  descinfo,
		CreatedAt: time.Now(),
	}

	err = tc.ThemeRepository.Create(theme)
	if err != nil {
		NewAPIError(&APIError{false, "Could not create theme", http.StatusBadRequest}, w)
		return
	}

	defer r.Body.Close()
	NewAPIResponse(&APIResponse{Success: true, Message: "Theme created", Data: theme}, w, http.StatusOK)
}

func (tc *ThemeController) Delete(w http.ResponseWriter, r *http.Request) {
	uid, err := services.UserIdFromContext(r.Context())
	if err != nil {
		NewAPIError(&APIError{false, "Something went wrong", http.StatusInternalServerError}, w)
		return
	}

	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		NewAPIError(&APIError{false, "Invalid request", http.StatusBadRequest}, w)
		return
	}

	subtask, err := tc.SubTaskRepository.FindByTid(id)
	if err == nil && subtask != nil {
		NewAPIError(&APIError{false, "Could not delete theme, the subtask has been generated", http.StatusBadRequest}, w)
		return
	}

	task, err := tc.TaskRepository.FindByTid(id)
	if err == nil && task != nil {
		NewAPIError(&APIError{false, "Could not delete theme, the task has been generated", http.StatusBadRequest}, w)
		return
	}

	theme, err := tc.ThemeRepository.FindById(id)
	if err != nil {
		NewAPIError(&APIError{false, "Could not find theme", http.StatusNotFound}, w)
		return
	}

	if uid != theme.UID {
		NewAPIError(&APIError{false, "Could not delete theme, Must be a theme creator", http.StatusBadRequest}, w)
		return
	}

	err = tc.ThemeRepository.Delete(id)
	if err != nil {
		NewAPIError(&APIError{false, "Could not delete theme", http.StatusBadRequest}, w)
		return
	}

	NewAPIResponse(&APIResponse{Success: true, Message: "Theme deleted", Data: theme}, w, http.StatusOK)
}
